package com.docmgmt.mvp.controller;

import com.docmgmt.mvp.dto.CommentVO;
import com.docmgmt.mvp.dto.CreateCommentRequest;
import com.docmgmt.mvp.dto.Result;
import com.docmgmt.mvp.entity.CommentEntity;
import com.docmgmt.mvp.entity.FileEntity;
import com.docmgmt.mvp.exception.ForbiddenException;
import com.docmgmt.mvp.exception.NotFoundException;
import com.docmgmt.mvp.exception.UnauthorizedException;
import com.docmgmt.mvp.mapper.CommentMapper;
import com.docmgmt.mvp.mapper.FileMapper;
import com.docmgmt.mvp.service.PermissionService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Comment Management Controller
 *
 * @author Dora
 */
@Slf4j
@RestController
@RequestMapping("/api/v1/files")
@RequiredArgsConstructor
public class CommentController {

    private final CommentMapper commentMapper;
    private final FileMapper fileMapper;
    private final PermissionService permissionService;

    /**
     * 4.7.1 Add Comment
     *
     * POST /api/v1/files/{id}/comments
     */
    @PostMapping("/{fileId}/comments")
    @Transactional(rollbackFor = Exception.class)
    public Result<CommentVO> addComment(
            @PathVariable Long fileId,
            @Valid @RequestBody CreateCommentRequest request) {
        Long userId = getCurrentUserId();

        log.info("POST /api/v1/files/{}/comments - userId={}, parentId={}",
                fileId, userId, request.getParentId());

        // Check file exists
        FileEntity file = fileMapper.selectById(fileId);
        if (file == null) {
            throw new NotFoundException("File not found");
        }

        // Check permission
        if (!permissionService.checkPermission(userId, "file:" + fileId, "read")) {
            throw new ForbiddenException("No permission to comment on this file");
        }

        // Create comment
        CommentEntity comment = CommentEntity.builder()
                .fileId(fileId)
                .parentId(request.getParentId() != null ? request.getParentId() : 0L)
                .content(request.getContent())
                .creatorId(userId)
                .isDeleted((byte) 0)
                .createdAt(LocalDateTime.now())
                .updatedAt(LocalDateTime.now())
                .build();

        commentMapper.insert(comment);

        log.info("Comment added: id={}", comment.getId());

        return Result.success("Comment added successfully", entityToVO(comment));
    }

    /**
     * 4.7.2 List File Comments
     *
     * GET /api/v1/files/{id}/comments
     */
    @GetMapping("/{fileId}/comments")
    public Result<List<CommentVO>> listFileComments(@PathVariable Long fileId) {
        log.info("GET /api/v1/files/{}/comments", fileId);

        // Check file exists
        FileEntity file = fileMapper.selectById(fileId);
        if (file == null) {
            throw new NotFoundException("File not found");
        }

        // Query top-level comments
        List<CommentEntity> topComments = commentMapper.listTopLevelByFileId(fileId);

        // Build comment tree
        List<CommentVO> result = topComments.stream()
                .map(comment -> {
                    CommentVO vo = entityToVO(comment);
                    // Load replies
                    vo.setReplies(loadReplies(comment.getId()));
                    return vo;
                })
                .collect(Collectors.toList());

        return Result.success("Query successful", result);
    }

    /**
     * 4.7.3 Delete Comment
     *
     * DELETE /api/v1/files/{fileId}/comments/{commentId}
     */
    @DeleteMapping("/{fileId}/comments/{commentId}")
    @Transactional(rollbackFor = Exception.class)
    public Result<Void> deleteComment(
            @PathVariable Long fileId,
            @PathVariable Long commentId) {
        Long userId = getCurrentUserId();

        log.info("DELETE /api/v1/files/{}/comments/{} - userId={}", fileId, commentId, userId);

        // Query comment
        CommentEntity comment = commentMapper.selectById(commentId);
        if (comment == null) {
            throw new NotFoundException("Comment not found");
        }

        // Check permission (only creator can delete)
        if (!comment.getCreatorId().equals(userId)) {
            throw new ForbiddenException("Only comment creator can delete it");
        }

        // Soft delete
        comment.setIsDeleted((byte) 1);
        comment.setUpdatedAt(LocalDateTime.now());
        commentMapper.updateById(comment);

        log.info("Comment deleted: commentId={}", commentId);

        return Result.success("Comment deleted successfully");
    }

    /**
     * Load replies recursively
     */
    private List<CommentVO> loadReplies(Long parentId) {
        List<CommentEntity> replies = commentMapper.listByParentId(parentId);
        
        return replies.stream()
                .map(reply -> {
                    CommentVO vo = entityToVO(reply);
                    // Recursively load nested replies
                    vo.setReplies(loadReplies(reply.getId()));
                    return vo;
                })
                .collect(Collectors.toList());
    }

    /**
     * Entity to VO
     */
    private CommentVO entityToVO(CommentEntity entity) {
        return CommentVO.builder()
                .id(entity.getId())
                .fileId(entity.getFileId())
                .parentId(entity.getParentId())
                .content(entity.getContent())
                .creatorId(entity.getCreatorId())
                .creatorName("Unknown User")  // TODO: query user table
                .createdAt(entity.getCreatedAt() != null ?
                        entity.getCreatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) : null)
                .updatedAt(entity.getUpdatedAt() != null ?
                        entity.getUpdatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) : null)
                .replies(new ArrayList<>())
                .build();
    }

    /**
     * Get current user ID from Spring Security context
     */
    private Long getCurrentUserId() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            throw new UnauthorizedException("Not logged in or authentication expired");
        }

        Object principal = authentication.getPrincipal();
        if (principal instanceof Long) {
            return (Long) principal;
        }

        throw new UnauthorizedException("Invalid authentication information");
    }
}
